package ga_helper

import (
	"math/rand"
    "time"
	"GA_ASCON/ascon_helper"
	"fmt"
	"sort"
	"math"
)

func Cross(pop [][]uint8,pc float32) {
	s := rand.New(rand.NewSource(time.Now().UnixNano()))
	n_pop := len(pop)
	index := []uint8{0,1,2,3,4,5,6,7}
	for i:=0;i<n_pop;i++{
		in1, in2 := s.Intn(n_pop),s.Intn(n_pop)
		r := s.Float32()
		if r < pc {
			n_byte := s.Intn(8)
			s.Shuffle(len(index),func(i,j int){index[i],index[j]=index[j],index[i]})
			inn := index[:n_byte]
			for j:=0;j<n_byte;j++{
				pop[in1][inn[j]],pop[in2][inn[j]] = pop[in2][inn[j]],pop[in1][inn[j]]
			}
		}
	}
}
type CorrF struct{
	Corcoe float32
	Key    uint8
}

func Coef(labels []uint8, predicts []float32) float32{
	fenz := float64(0)
	fenmu_X := float64(0.0)
	fenmu_Y := float64(0.0)
	average_X := float64(0.0)
	average_Y := float64(0.0)
	for i:=0;i<len(labels);i++{
		average_X+=float64(labels[i])
		average_Y+=float64(predicts[i])
	}
	average_X /= float64(len(labels))
   	average_Y /= float64(len(labels))

	for i:=0;i<len(labels);i++{
		fenz   += (float64(labels[i]) - average_X)*(float64(predicts[i]) - average_Y)
		fenmu_X   += (float64(labels[i]) - average_X)*(float64(labels[i]) - average_X)
		fenmu_Y   += (float64(predicts[i]) - average_Y)*(float64(predicts[i]) - average_Y)
	}
	Pear := math.Abs(fenz/(math.Sqrt(fenmu_X)*(math.Sqrt(fenmu_Y))))
	return float32(Pear)
}

func Score(labels []uint8, predicts [][]float32) float32{
	var pred float64
	for i:=0;i<len(labels);i++{
		pred += math.Log(float64(predicts[i][labels[i]])+1e-36)
	}
	return float32(pred)
}

func Mutation(pop [][]uint8,pm float32, nonce0s, nonce1s [][]uint8, predicts []float32, fit func(a []uint8,b []float32) float32) {
	n_pop := len(pop)
	s := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i:=0;i<n_pop;i++{
		for j:=0;j<8;j++{
			if s.Float32()<pm{
				tmp := pop[i]
				klf := make([]CorrF,0)
				for k:=0;k<256;k++{
					tmp[j] = uint8(k)
					labels_y := ascon_helper.Computer_Sbox_labels(tmp,nonce0s,nonce1s,64)
					Hws := ascon_helper.Hw_weights(labels_y)
					fitness:= fit(Hws,predicts)
					klf = append(klf,CorrF{fitness,uint8(k)})
				}

				sort.SliceStable(klf, func(i int, j int) bool {return klf[i].Corcoe > klf[j].Corcoe})
				pop[i][j] = klf[0].Key
			}
		}
	}
}

func Mutation_c(pop [][]uint8,pm float32, nonce0s, nonce1s [][]uint8, predicts [][]float32, fit func(a []uint8,b [][]float32) float32, flag_2 bool) {
	n_pop := len(pop)
	s := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i:=0;i<n_pop;i++{
		for j:=0;j<8;j++{
			if s.Float32()<pm{
				tmp := pop[i]
				klf := make([]CorrF,0)
				for k:=0;k<256;k++{
					tmp[j] = uint8(k)
					labels_y := ascon_helper.Computer_Sbox_labels(tmp,nonce0s,nonce1s,64)
					if flag_2{//2分类
						Hws := ascon_helper.HW_2c(labels_y)
						fitness:=fit(Hws,predicts)
						klf = append(klf,CorrF{fitness,uint8(k)})
					}else{//65分类
						Hws := ascon_helper.Hw_weights(labels_y)
						fitness:=fit(Hws,predicts)
						klf = append(klf,CorrF{fitness,uint8(k)})
					}
				}
				sort.SliceStable(klf, func(i int, j int) bool {
					return klf[i].Corcoe > klf[j].Corcoe
				})
				
				pop[i][j] = klf[0].Key
				
			}
		}
	}
}

type Fit_Key struct{
	Fitn float32
	Key  []uint8
}

func Selection(pop [][]uint8,fintess []Fit_Key){
	lens := len(fintess)
	// fmt.Printf("Pop_C:%v\n",fintess)
	s := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i:=0;i<lens;i++{
		in1,in2 := s.Intn(lens),s.Intn(lens)
		if fintess[in1].Fitn>fintess[in2].Fitn{
			// copy(pop[i],pop_copy[in1])
			pop[i] = fintess[in1].Key
		}else{
			// copy(pop[i],pop_copy[in2])
			pop[i] = fintess[in2].Key
		}
	}
}

func IntToBinary(num uint8) []uint8 {
    binary := make([]uint8,8)
	if num == 0 {return binary}
	i:=7
    for ;num > 0; num = num / 2{
        bit := num % 2
        binary[i] = bit
		i--
    }
    return binary
}

func Bytes2bin(a []uint8) []uint8{
	var tmp [][]uint8
	for i:=0;i<len(a);i++{
		tmp = append(tmp,IntToBinary(a[i]))
	}
	re := make([]uint8,len(tmp)*len(tmp[0]))
	for i:=0;i<len(tmp);i++{
		for j:=0;j<len(tmp[0]);j++{
			re[i*len(tmp[0])+j] = tmp[i][j]
		}
	}
	return re
}


func Num_same(a[]uint8,b[]uint8) uint8{
	if len(a)!=len(b){panic(fmt.Errorf("数据度度不等"))}
	var count uint8
	for i:=0;i<len(a);i++{
		if a[i]==b[i]{
			count++
		}
	}
	return count
}

type Fit_S struct{
	Fitn float32
	Key  []uint8
	Num_bytes uint8
	Num_bits  uint8
}

func Result (correct_key []uint8, fit_key []Fit_Key) []Fit_S{
	var result []Fit_S
	lens := len(fit_key)
	bin_correct_key:=Bytes2bin(correct_key)
	for i:=0;i<lens;i++{
		fitness:=fit_key[i].Fitn
		key := fit_key[i].Key
		bin_key:=Bytes2bin(key)
		num_bytes := Num_same(correct_key,key)
		num_bits := Num_same(bin_correct_key,bin_key)
		result = append(result,Fit_S{fitness,key,num_bytes,num_bits})
	}
	sort.SliceStable(result, func(i int, j int) bool {return result[i].Num_bits > result[j].Num_bits})
	return result
}